#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <sys/time.h>

#define MAXBUFFERSIZE 1024
#define CONNECTION_SIZE 1048576
#define MAX_PORTS 20

typedef int (*rcallback)(int fd);
#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

int epfd = 0;
struct timeval begin;

enum _EPOLL_CTRL
{
    ADD,
    MOD
};

struct conn
{
    int fd;
    char rbuffer[MAXBUFFERSIZE];
    int rlength;
    char wbuffer[MAXBUFFERSIZE];
    int wlength;
    rcallback send_callback;
    union
    {
        rcallback recv_callback;
        rcallback accept_callback;
    } r_action;
} conn_list[CONNECTION_SIZE];

int Init_server(int port);
// c语言定义的枚举，传参时要加enum
void set_event(int fd, enum EPOLL_EVENTS events, enum _EPOLL_CTRL ctrl);
int accept_cb(int fd);
int recv_cb(int fd);
int send_cb(int fd);

int main()
{
    epfd = epoll_create(1);

    for (int i = 0; i < MAX_PORTS; i++)
    {
        int listenfd = Init_server(9999 + i);
        conn_list[listenfd].fd = listenfd;
        conn_list[listenfd].r_action.accept_callback = accept_cb;
        set_event(listenfd, EPOLLIN, ADD);
    }

    gettimeofday(&begin,NULL);

    struct epoll_event evs[MAXBUFFERSIZE] = {0};
    while (1)
    {
        int readyfds = epoll_wait(epfd, evs, MAXBUFFERSIZE, -1);
        for (int i = 0; i < readyfds; i++)
        {
            int connfd = evs[i].data.fd;
            if (evs[i].events & EPOLLIN)
            {
                // 这里是知识盲区
                // 函数指针本质上是一个指向函数代码起始地址的指针。
                // 在联合体中，多个函数指针共享同一块内存，因此它们实际上指向的是同一个地址。
                // 这意味着，在联合体中，即使定义了多个函数指针，它们也只能同时指向一个函数
                conn_list[connfd].r_action.accept_callback(connfd);
            }
            if (evs[i].events & EPOLLOUT)
            {
                conn_list[connfd].send_callback(connfd);
            }
        }
    }

    for (int i = 0; i < 20; i++)
    {
        close(i + 3);
    }

    return 0;
}

int Init_server(int port)
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in sockserver;
    memset(&sockserver, 0, sizeof(sockserver));
    sockserver.sin_family = AF_INET;
    sockserver.sin_addr.s_addr = INADDR_ANY;
    sockserver.sin_port = htons(port);
    bind(listenfd, (struct sockaddr *)&sockserver, sizeof(struct sockaddr_in));

    listen(listenfd, 10);
    // printf("listenfd：%d\n", listenfd);
    return listenfd;
}

void set_event(int fd, enum EPOLL_EVENTS events, enum _EPOLL_CTRL ctrl)
{
    struct epoll_event ev;
    ev.events = events;
    ev.data.fd = fd;
    epoll_ctl(epfd, ctrl == ADD ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ev);
}

int accept_cb(int fd)
{
    struct sockaddr_in sockclient;
    socklen_t len = sizeof(sockclient);
    int clientfd = accept(fd, (struct sockaddr *)&sockclient, &len);
    set_event(clientfd, EPOLLIN, ADD);
    // printf("listen %d\tclientfd: %d\n", fd, clientfd);

    memset(conn_list[clientfd].rbuffer, 0, sizeof(conn_list[clientfd].rbuffer));
    conn_list[clientfd].rlength = 0;
    memset(conn_list[clientfd].wbuffer, 0, sizeof(conn_list[clientfd].wbuffer));
    conn_list[clientfd].wlength = 0;
    conn_list[clientfd].fd = clientfd;
    conn_list[clientfd].r_action.recv_callback = recv_cb;
    conn_list[clientfd].send_callback = send_cb;

    if (clientfd % 1000 == 0)
    {
        struct timeval current;
        gettimeofday(&current,NULL);
        int time_used = TIME_SUB_MS(current, begin);
        memcpy(&begin,&current,sizeof(struct timeval));
        //printf("accept lientfd: %d\t time_used: %d\n", clientfd,time_used);
    }
    return clientfd;
}

int recv_cb(int fd)
{
    int recvsize = recv(fd, conn_list[fd].rbuffer, MAXBUFFERSIZE, 0);
    conn_list[fd].rlength = recvsize;
    if (0 == recvsize)
    {
        close(fd);
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        //printf("client disconnected:%d\n", fd);
        return 0;
    }
    memcpy(conn_list[fd].wbuffer, conn_list[fd].rbuffer, recvsize);
    conn_list[fd].wlength = recvsize;

    set_event(fd, EPOLLOUT, MOD);

    //printf("client %d say: %s\n", fd, conn_list[fd].rbuffer);
    return recvsize;
}

int send_cb(int fd)
{
    int sedsize = send(fd, conn_list[fd].wbuffer, conn_list[fd].wlength, 0);
    //memset(conn_list[fd].wbuffer,0,conn_list[fd].wlength);
    //坑：折磨了许久，就一个宏写错的原因。导致跑3000多就不动了
    set_event(fd, EPOLLIN, MOD);
    return sedsize;
}